package com.jlf.core.server.functional;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.invoke.*;
import java.lang.reflect.Method;

/**
 * 函数接口辅助工具
 *
 * @author wujr
 * 2023/7/22
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/22 1.0 新增]
 */
@Slf4j
public class FunctionalTool {
    @SuppressWarnings("unchecked")
    public static <T> SimpleConsumer<T> simpleConsumer(Method method, Object bean) throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle handle = lookup.unreflect(method);
        MethodType invokedType = MethodType.methodType(SimpleConsumer.class, bean.getClass());
        MethodType sameType = MethodType.methodType(void.class, Object.class);
        MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "accept",
                invokedType,
                sameType,
                handle,
                functionMethodType);
        return (SimpleConsumer<T>)callSite.getTarget().bindTo(bean).invoke();
    }
    @SuppressWarnings("unchecked")
    public static <T> SimpleResultConsumer<T> simpleResultConsumer(Method method, Object bean) throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle handle = lookup.unreflect(method);
        MethodType invokedType = MethodType.methodType(SimpleResultConsumer.class, bean.getClass());
        MethodType sameType = MethodType.methodType(void.class, Object.class, boolean.class);
        MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "accept",
                invokedType,
                sameType,
                handle,
                functionMethodType);
        return (SimpleResultConsumer<T>)callSite.getTarget().bindTo(bean).invoke();
    }
    @SuppressWarnings("unchecked")
    public static <T> SimpleConsumer2<T> simpleConsumer2(Method method, Object bean) throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle handle = lookup.unreflect(method);
        MethodType invokedType = MethodType.methodType(SimpleConsumer2.class, bean.getClass());
        MethodType sameType = MethodType.methodType(void.class, Object.class, Object.class);
        MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "accept",
                invokedType,
                sameType,
                handle,
                functionMethodType);
        return (SimpleConsumer2<T>)callSite.getTarget().bindTo(bean).invoke();
    }
    @SuppressWarnings("unchecked")
    public static <T> SimpleResultConsumer2<T> simpleResultConsumer2(Method method, Object bean) throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle handle = lookup.unreflect(method);
        MethodType invokedType = MethodType.methodType(SimpleResultConsumer2.class, bean.getClass());
        MethodType sameType = MethodType.methodType(void.class, Object.class, Object.class, boolean.class);
        MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "accept",
                invokedType,
                sameType,
                handle,
                functionMethodType);
        return (SimpleResultConsumer2<T>)callSite.getTarget().bindTo(bean).invoke();
    }

    @SuppressWarnings("unchecked")
    public static <T, R> SFunction<T, R> getter(Class<T> objectClass, String fieldName, Class<R> returnType){
        try {
            Method method = objectClass.getDeclaredMethod("get" + StringUtils.capitalize(fieldName));
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle handle = lookup.unreflect(method);
            MethodType invokedType = MethodType.methodType(SFunction.class);
            MethodType sameType = MethodType.methodType(returnType, objectClass);
            MethodType functionMethodType = MethodType.methodType(returnType, objectClass);
            CallSite callSite = LambdaMetafactory.metafactory(lookup, "apply",
                    invokedType, sameType, handle, functionMethodType);
            return (SFunction<T, R>)callSite.getTarget().invokeExact();
        }catch (Throwable e){
            log.error("获取类：{} 的属性：{} 的getter方法，该属性不存在", objectClass.getSimpleName(), fieldName);
            throw new RuntimeException("类的属性不存在");
        }
    }
}
